using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Text;
using Agile.Common.UI;
using GeneratorCustomization;
using Microsoft.Practices.Unity;
using Agile.Common.Loggers;

namespace Agile.Genie.Generators
{
    /// <summary>
    /// This is the base class for all generators.
    /// </summary>
    public abstract class Generator : IAgileControlDetails
    {
        #region Constructors and Initializers

        /// <summary>
        /// Instantiate a new generator, specifiy the subsystem
        /// the generated code belongs to.
        /// </summary>
        /// <remarks>
        /// Protected constructor so instantiation of inherited objects
        /// is locked down, forcing the use of the Instantiate function(s)
        /// </remarks>
        protected Generator()
        {
            InitializeDelegates();
            RegisterTableNamePrefixesToRemove();
        }

        #endregion

        private static DataRowCollection _tableNamePrefixesToRemove;


        #region IAgileControlDetails Members

        /// <summary>
        /// Gets any child objects.
        /// </summary>
        /// <example>A database table should return its collection of columns.</example>
        /// <remarks>Returns NULL if there are not any child objects.</remarks>
        IList IAgileControlDetails.ChildObjects
        {
            get
            {
                // TODO:  Add Generator.ChildObjects getter implementation
                return null;
            }
        }

        /// <summary>
        /// Gets the value to display in the control
        /// </summary>
        string IAgileControlDetails.DisplayValue
        {
            get { return GetType().Name; }
        }

        /// <summary>
        /// Gets the color to use for the fore color
        /// </summary>
        /// <remarks>Must be an existing color, may be null</remarks>
        public string ForeColor
        {
            get { return null; }
        }

        /// <summary>
        /// Gets the color to use for the back color
        /// </summary>
        /// <remarks>Must be an existing color, may be null</remarks>
        public string BackColor
        {
            get { return null; }
        }

        #endregion

        /// <summary>
        /// Generates all of the code.
        /// </summary>
        public virtual string Generate()
        {
//            try
//            {
            var generatedCode = new StringBuilder(GeneratedStamp);
            generatedCode.Append(GenerateFileHeaderComments());
            // TODO: Change Generate so it is not abstract or virtual, create abstract GenerateInternal() method
            return generatedCode.ToString();
//            }
//            catch (Exception ex)
//            {
//                Debug.WriteLine(ex.Message);
//                return new StringBuilder(string.Format("An exception occurred whilst generating:- \r\n \r\n{0}", ex.Message)).ToString();
//            }
        }

        /// <summary>
        /// Generates the file header comments for the file
        /// </summary>
        /// <returns></returns>
        public virtual string GenerateFileHeaderComments()
        {
            return string.Format(@"
Purpose:    {0}
Info:       {1}"
                                 , FilePurpose
                                 , FileNotes);
        }

        /// <summary>
        /// Register the table name prefixes that are to be removed from Clean Class Names.
        /// (from the GeneratorsDetailsData table 'TableNamePrefixesToRemove')
        /// </summary>
        private static void RegisterTableNamePrefixesToRemove()
        {
            if (_tableNamePrefixesToRemove != null)
                return;

            _tableNamePrefixesToRemove = GeneratorsData.GetDataRowsFor("TableNamePrefixesToRemove");

            if (_tableNamePrefixesToRemove.Count == 0)
                return;

            var remove = new string[_tableNamePrefixesToRemove.Count];
            int i = 0;
            foreach (DataRow row in _tableNamePrefixesToRemove)
            {
                remove[i] = row["Prefix"].ToString();
                i++;
            }
//            DatabaseTable.RegisterTableNamePrefixesToRemove(remove);
        }

        #region abstract 

        /// <summary>
        /// Gets the description of the purpose of the file.
        /// </summary>
        protected abstract string FilePurpose { get; }

        /// <summary>
        /// Gets a string containing additional notes about the files contents.
        /// </summary>
        protected abstract string FileNotes { get; }

        /// <summary>
        /// Gets the name of the file that is to be created, not including the file extension.
        /// </summary>
        /// <example>SimpleClass or SimpleStoredProc</example>
        public abstract string FileName { get; }

        /// <summary>
        /// Gets the file extension of the file that is to be created.
        /// </summary>
        /// <example>.cs or .prc</example>
        public abstract string FileExtension { get; }

        /// <summary>
        /// Initialize all delegates for code generation. 
        /// i.e. set the strategy pattern handlers.
        /// </summary>
        protected abstract void InitializeDelegates();


        #endregion

        #region File and Directory

        private GeneratedFileInfo _generatedFile;

        /// <summary>
        /// Gets the name of the file, including the extension.
        /// </summary>
        /// <example>SimpleClass.cs or SimpleStoredProc.prc</example>
        public string FullFileName
        {
            get { return FileName + FileExtension; }
        }

        /// <summary>
        /// Gets the generated file.
        /// </summary>
        /// <remarks>Throws an exception if the file does not already exist.</remarks>
        public GeneratedFileInfo GeneratedFile
        {
            get
            {
                if (_generatedFile == null)
                {
                    string path = Path.Combine(FileGenerationDirectory.FullName, FileName + FileExtension);
                    _generatedFile = GeneratedFileInfo.Build(path, GeneratedStamp);
                    _generatedFile.Logger = Logger;
                }
                return _generatedFile;
            }
        }


        /// <summary>
        /// Gets the base directory for file generation.
        /// </summary>
        /// <remarks>i.e. The base directory that the file will be generated in.</remarks>
        public virtual DirectoryInfo FileGenerationDirectory
        {
            get
            {
                return new DirectoryInfo(Path.Combine(FileOutputPathRoot, FileGenerationDirectoryDetails));

            }
        }

        /// <summary>
        /// Gets the additional information that is required to save the generated file
        /// in the correct directory.
        /// e.g. "Northwind\SQL\StoredProcs"
        /// </summary>
        /// <remarks>i.e. The base directory that the file will be generated in.</remarks>
        protected abstract string FileGenerationDirectoryDetails { get; }

        /// <summary>
        /// Gets the 'stamp' string that is used to identify the file 
        /// as a generated file.
        /// This is imporatant because it stops the generator from 
        /// accidently overwriting any existing files which may have existed 
        /// previously but were not generated.
        /// </summary>
        /// <remarks>This should be overwritten and the code types 'comment' tag
        /// needs to be prefix the base.GeneratedStamp.</remarks>
        public virtual string GeneratedStamp
        {
            get { return GeneratorDataFor("GeneratedStampText") + @"
"; }
        }

        private string fileOutputPathRoot;

        /// <summary>
        /// Gets the ROOT path for file output (where the generated files will go)
        /// </summary>
        public string FileOutputPathRoot
        {
            get 
            {
                if (fileOutputPathRoot == null)
                {
                    fileOutputPathRoot = GetFileOutputPathRoot();
                }
                return fileOutputPathRoot; 
            }
        }

        /// <summary>
        /// get the GetFileOutputPathRoot
        /// </summary>
        public static string GetFileOutputPathRoot()
        {
            DirectoryInfo currentDir = new DirectoryInfo(Environment.CurrentDirectory);
            return Path.Combine(currentDir.Parent.Parent.Parent.FullName, "_GenieOutput");
        }

        /// <summary>
        /// Runs 'Generate' and saves the generated code to file.
        /// </summary>
        public virtual void SaveToFile()
        {
            var code = new StringBuilder(Generate());
            GeneratedFile.WriteToFile(code.ToString());
        }

        #endregion

        #region Generator Data

        /// <summary>
        /// Gets the data from the xml file for the specified column in the specified table.
        /// </summary>
        /// <param name="tableName">Name of the table in the xml data file.
        /// This is usually going to be the name of the Generator sub class.</param>
        /// <param name="columnName">Name of the column in the xml data file.</param>
        /// <remarks>Each class that inherits from Generator that requries some
        /// variable data to be stored needs to create it's own table in 'GeneratorsDetails'.
        /// <para>GeneratorsDetailsDataItem is only useful if there is only a single row in the table.</para>
        /// <para>In the class, call this function from its own 'NewGeneratorClass'DataFor() method, feeding in the name of the class as the table name.</para></remarks>
        protected static string GetGeneratorsDataItem(string tableName, string columnName)
        {
            if (GeneratorsData.All.Tables[tableName].Rows.Count != 1)
                return string.Empty;
            return GeneratorsData.All.Tables[tableName].Rows[0][columnName].ToString();
        }


        /// <summary>
        /// Gets the data from the xml file for the specified column in the specified table.
        /// </summary>
        /// <param name="columnName">Name of the column in the xml data file (that is in the 'Generator' table).</param>
        protected static string GeneratorDataFor(string columnName)
        {
            return GetGeneratorsDataItem("Generator", columnName);
        }

        #endregion
        // set the default logger
        private ILogger logger = new DebugLogger();

        [Dependency]
        public ILogger Logger
        {
            get { return logger; }
            set { logger = value; }
        }
    }
}